package linkedhashset;

import java.util.LinkedHashSet;

public class LinkedHashSet_ {
    public static void main(String[] args) {
        /**
         * 1.LinkedHashSet是HashSet的子类
         * 2.LinkedHashSet底层是一个LinkedHashMap，底层维护了一个数组+双向链表
         * 3.LinkedHashSet根据元素的hashCode值来决定元素的存储位置，同时使用链表维护元素的次序，所以元素的插入与取出顺序一致
         * 4.LinkedHashSet不允许添加重复元素
         * 5.
         */
        LinkedHashSet linkedHashSet = new LinkedHashSet();

        //[我, 是, 大, 帅哥]
        /**
         * 1.调用构造方法
         * todo
         *      public LinkedHashSet() {
         *         super(16, .75f, true);
         *     }
         * 2.因为继承自HashSet，所以调用到HashSet的构造方法
         * todo HashSet类
         *      HashSet(int initialCapacity, float loadFactor, boolean dummy) {
         *         map = new LinkedHashMap<>(initialCapacity, loadFactor);
         *     }
         * 3.可以看到底层又调用了LinkedHashMap
         * todo LinkedHashMap类
         *      public LinkedHashMap(int initialCapacity, float loadFactor) {
         *         super(initialCapacity, loadFactor);
         *         accessOrder = false;
         *     }
         * 4.LinkedHashMap又继承自HashMap调用到父类的构造器
         * todo 至此完成了一个LinkedHashSet对象的创建
         *      public HashMap(int initialCapacity, float loadFactor) {
         *         if (initialCapacity < 0)
         *             throw new IllegalArgumentException("Illegal initial capacity: " +
         *                                                initialCapacity);
         *         if (initialCapacity > MAXIMUM_CAPACITY)
         *             initialCapacity = MAXIMUM_CAPACITY;
         *         if (loadFactor <= 0 || Float.isNaN(loadFactor))
         *             throw new IllegalArgumentException("Illegal load factor: " +
         *                                                loadFactor);
         *         this.loadFactor = loadFactor;
         *         this.threshold = tableSizeFor(initialCapacity);
         *     }
         */

        /**
         *
         */
        linkedHashSet.add("我");
        linkedHashSet.add("是");
        linkedHashSet.add("大");
        linkedHashSet.add("帅哥");
        linkedHashSet.add("帅哥");
        linkedHashSet.add("帅哥");
        System.out.println(linkedHashSet);

        /**
         * 添加元素
         *
         * 添加元素执行的还是HashSet那一条线,没有差异
         * LinkedHashSet 底层维护的是一个LinkedHashMap(是HashMap的子类)
         * 第一次添加数据 直接将数组table扩容到16
         * 同样的在LinkedHashSet中维护了一个 数组table+双向链表
         * table数组 : HashMap$Node
         * 其子项是  : LinkedHashMap$Entry
         * 可推断LinkedHashMap$Entry必然继承了HashMap$Node
         * todo LinkedHashMap$Entry 是一个静态内部类
         *      static class Entry<K,V> extends HashMap.Node<K,V> {
         *         Entry<K,V> before, after;
         *         Entry(int hash, K key, V value, Node<K,V> next) {
         *             super(hash, key, value, next);
         *         }
         *     }
         *
         * 而它又继承自HashMap中的静态内部类Node，从Node可通过HashMap类名访问也可以看出
         * todo
         *      static class Node<K,V> implements Map.Entry<K,V> {...}
         */

        //按对象移除
        linkedHashSet.remove("我");
        System.out.println(linkedHashSet);
        /**
         * 1.调用remove()
         * todo
         *      public boolean remove(Object o) {
         *         return map.remove(o)==PRESENT;
         *     }
         * 2.来到HashMap中的remove方法
         * todo
         *      public V remove(Object key) {
         *         Node<K,V> e;
         *         return (e = removeNode(hash(key), key, null, false, true)) == null ?
         *             null : e.value;
         *     }
         * 3.计算出该对象的hash值，去removeNode中移除这个节点
         * todo  HashMap类
         *      final Node<K,V> removeNode(int hash, Object key, Object value,
         *                                boolean matchValue, boolean movable) {
         *         Node<K,V>[] tab; Node<K,V> p; int n, index;
         *         //散列表不为空 && 散列表长度大于0 && 此对象的hash值对应的索引位不是空对象
         *         if ((tab = table) != null && (n = tab.length) > 0 &&
         *             (p = tab[index = (n - 1) & hash]) != null) {
         *             Node<K,V> node = null, e; K k; V v;
         *             //再确认当前位置的对象hash值与要移除的对象的hash值相等
         *             //并且 - 两个对象相等（比较地址）|| 两对象的值相等
         *             if (p.hash == hash &&
         *                 ((k = p.key) == key || (key != null && key.equals(k))))
         *                 node = p;
         *             else if ((e = p.next) != null) {
         *                 if (p instanceof TreeNode)
         *                     node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
         *                 else {
         *                 //找到要删对象的前一个节点
         *                     do {
         *                         if (e.hash == hash &&
         *                             ((k = e.key) == key ||
         *                              (key != null && key.equals(k)))) {
         *                             node = e;
         *                             break;
         *                         }
         *                         p = e;
         *                     } while ((e = e.next) != null);
         *                 }
         *             }
         *             //找到了要删除的节点
         *             if (node != null && (!matchValue || (v = node.value) == value ||
         *                                  (value != null && value.equals(v)))) {
         *                 if (node instanceof TreeNode)
         *                     ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
         *                 //再次确认要删除的对象 如果正好是当前索引位置的散列表的第一个元素
         *                 else if (node == p)
         *                      //将此节点的下一个元素赋值给当前索引位置，相当于把这个对象给删了
         *                     tab[index] = node.next;
         *                 else
         *                      //如果要删的不是第一个元素，将要删对象的前一个节点指向要删对象的后一个节点
         *                      //p 的值是 要删对象的前一个节点
         *                     p.next = node.next;
         *                 ++modCount;
         *                 --size;
         *                 // 这个函数是去调整双向链表的指向
         *                 afterNodeRemoval(node);
         *                 return node;
         *             }
         *         }
         *         return null;
         *     }
         *
         *  4.调整双向链表的指向
         *  todo 回到了Linked
         *         void afterNodeRemoval(Node<K,V> e) { // unlink
         *         LinkedHashMap.Entry<K,V> p =
         *             (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
         *         p.before = p.after = null;
         *         if (b == null)
         *             head = a;
         *         else
         *             b.after = a;
         *         if (a == null)
         *             tail = b;
         *         else
         *             a.before = b;
         *     }
         *
         */
    }
}
